#include "StdAfx.h"
#include "policyFileParser.h"
#include <sstream>
#include <iostream>
#include <fstream>
#include <windows.h>

//////////////////////////////////////////////////////////////////////////
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	13/05/2013
//Uses uses policy files to determine what action to take
//Needs to keep a log of each action and observation that there has
//occurred, this is stored is a map where the key is the node name and
//the value is the outcome, this is used to build the search strings
//to search for in the policy file
//
//Update 1
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	14/05/2014
//New constructor that loads policy files into memory upon launch and uses
//files in memory to search policies using a map of filename to list of
//strings
//
//Update 2
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	14/05/2014
//Pre-loading pre-loads the policy into memory stored as a hash_map 
//by splitting the policy strings into two parts, the key is the 
//combination part of the string and the value is the action part of
//the string.
//e.g. map<string, string>
//
//Update 3
//Author	:	Ross Conroy	ross.conroy@tees.ac.uk
//Date		:	10/06/2014
//Updated to allow for enemy agent actions to also be logged to aid in
//determining what model the agent is following
//
//Update 4
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	15/09/2014
//Change to parse policy CSV files in new format to simplify the policy 
//format
//
//////////////////////////////////////////////////////////////////////////

PolicyFileParser::PolicyFileParser()
{
}

PolicyFileParser::PolicyFileParser(string policyFiles, string fileExtension, bool preloadFiles, string actionPrefix)
{
	_actionPrefix = actionPrefix;
	_policyFilesDir = policyFiles;
	_fileExtension = fileExtension;

	if(preloadFiles)
	{
		cout << "Pre-Loading Files" << endl;
		PreloadedFiles();
		cout << "Files Loaded" << endl;
	}
}

PolicyFileParser::PolicyFileParser(string fileExtension, bool preloadFiles, string actionPrefix)
{
	_actionPrefix = actionPrefix;
	_fileExtension = fileExtension;
}

void PolicyFileParser::ChangeDirectory(string folder)
{
	_policyFilesDir = folder;
}

//Pre-loads policy files
//loops through each file splitting the pairs ignoring the first line
void PolicyFileParser::PreloadedFiles()
{
	int numberOfFiles = GetNumberOfFilesInFolder(_policyFilesDir, _fileExtension);

	for(int i = 0; i < numberOfFiles; i++)
	{
		string file = GetActionNodeFileNameFromTimeStep2(i+1);
		string nodename = GetActionNodeFileNameFromTimeStep(i+1);
		list<string> lines = ParseTextFile(file);

		hash_map<string, string> policy;

		lines.pop_front();

		while(lines.size() > 0)
		{
			string row = lines.front();
			string key = "";
			string value = "";

			if(row != "")
			{
				int j = 0;
				while (row.find(",", 0) != string::npos)
				{
					size_t  pos = row.find(",", 0);
					string temp = row.substr(0, pos);
					row.erase(0, pos + 1);

					if(j == 0)
					{
						key = temp;
					}
					else if (j == 1)
					{
						value = temp;
					}

					j++;
				}
				/*if(key == "")
				{
					key = "-";
				}*/
				policy[key] = value;
			}
			lines.pop_front();
		}

		hash_map<string,hash_map<string,string>>::iterator itFind = _policies.find(file);

		if (_policies.find(file) != _policies.end())
		{
			_policies[nodename] = policy;
		}
		else
		{
			for (hash_map<string,string>::iterator itp = policy.begin(); itp != policy.end(); itp++)
			{
				string key = itp->first;
				string value = itp->second;
				_policies[nodename][key] = value;
			}
		}
		
	}
}

//Converts the text file into a list of strings for future parsing
//Update, check if file has already been loaded into memory
list<string> PolicyFileParser::ParseTextFile(string inputFile)
{
	list<string> lines;

	ifstream readFile;
	readFile.open(inputFile.c_str());
	if(readFile.is_open())
	{
		while(!readFile.eof())
		{
			string line;
			getline(readFile, line);
			lines.push_back(line);
		}

		readFile.close();
	}

	return lines;
}

//Used the time step and global action name to determine the name of
//the current action node for time step
string PolicyFileParser::GetActionNodeFileNameFromTimeStep(int timeStep)
{
	stringstream ss;
	ss << _actionPrefix << timeStep << _fileExtension;
	return ss.str();
}

string PolicyFileParser::GetActionNodeFileNameFromTimeStep2(int timeStep)
{
	stringstream ss;
	ss << _policyFilesDir << _actionPrefix << timeStep << _fileExtension;
	return ss.str();
}

//Finds the parent names near the beginning of the file and returns a list
//of the names of the parents in the order they appear in the file
list<string> PolicyFileParser::GetParentNamesFromFile(list<string> inputStrings)
{
	list<string> parents;

	bool readLine = false;
	bool breakfor = false;

	for(list<string>::iterator it = inputStrings.begin(); it != inputStrings.end(); it++)
	{
		string line = *it;

		//reached end of parents
		if(line == "POLICY")
		{
			readLine = false;
			breakfor = true;
		}

		if(breakfor)
		{
			break;
		}

		//If safe to read a line extract the parent name from it
		if(readLine)
		{
			string parentLine = line;
			int pos = parentLine.find_first_of(",");
			parentLine = parentLine.substr(0, pos);
			parents.push_back(parentLine);
		}

		//Hit start of parents
		if(line == "PARENTS")
		{
			readLine = true;
		}
	}

	return parents;
}

//Builds a search string then uses expected filename and search string as key to
//find appropriate action in CSV policy file that has been preloaded
string PolicyFileParser::DecisionFromObservation(int timeStep, string observation, string opponentAction)
{
	//Default Error, if successful then ERROR will be replaced
	stringstream ss;
	if(timeStep == 1)
	{
		_previousActObs.clear();
		_searchString = "";
		observation = "";
	}
	else
	{
		ss << _searchString << observation;
	}

	string action = "ERROR"; 	

	string actionFileName = GetActionNodeFileNameFromTimeStep(timeStep);

	if(_policies.count(actionFileName) > 0)
	{
		string searchStr = ss.str();
		/*if(searchStr == "")
		{
			searchStr = "-";
		}*/
		string tempAction = _policies[actionFileName][searchStr];

		//extract a single action from temp action using same to parse csv lines
		vector<string> actions;
		while (tempAction.find("-", 0) != string::npos)
		{
			size_t  pos = tempAction.find("-", 0);
			string temp = tempAction.substr(0, pos);
			tempAction.erase(0, pos + 1);
			actions.push_back(temp);
		}

		if (actions.size() > 0)
		{
			tempAction = actions[rand() % actions.size()];
		}	


		if(tempAction != "")
		{
			action = tempAction;
		}
		if(timeStep == 0)
		{
			ss << action << "-";
		}
		else
		{
			if (timeStep == 1)
			{
				ss << action << "-";
			}
			else
			{
				ss << "-" << action << "-";
			}
			
		}		
		_searchString = ss.str();
	}

	//cout << "Time Step - " <<timeStep <<  "  Observation - " << observation << "  Action - " << action << endl;

	return action;
}

string PolicyFileParser::DecisionFromObservation(int timeStep, string observation)
{
	//Default Error, if successful then ERROR will be replaced
	stringstream ss;
	if(timeStep == 1)
	{
		_previousActObs.clear();
		_searchString = "";
		observation = "";
	}
	else
	{
		ss << _searchString << observation;
	}

	string action = "ERROR"; 	

	string actionFileName = GetActionNodeFileNameFromTimeStep(timeStep);

	if(_policies.count(actionFileName) > 0)
	{
		string searchStr = ss.str();
		/*if(searchStr == "")
		{
			searchStr = "-";
		}*/
		string tempAction = _policies[actionFileName][searchStr];

		//extract a single action from temp action using same to parse csv lines
		vector<string> actions;
		while (tempAction.find("-", 0) != string::npos)
		{
			size_t  pos = tempAction.find("-", 0);
			string temp = tempAction.substr(0, pos);
			tempAction.erase(0, pos + 1);
			actions.push_back(temp);
		}

		if(actions.size() > 0)
		{
			tempAction = actions[rand() % actions.size()];
		}		


		if(tempAction != "")
		{
			action = tempAction;
		}
		if(timeStep == 0)
		{
			ss << action << "-";
		}
		else
		{
			if (timeStep == 1)
			{
				ss << action << "-";
			}
			else
			{
				ss << "-" << action << "-";
			}
			
		}		
		_searchString = ss.str();
	}

	//cout << "Time Step - " <<timeStep <<  "  Observation - " << observation << "  Action - " << action << endl;

	return action;
}

//Determines how many policy files are available
int PolicyFileParser::GetNumberOfFilesInFolder(string folderDir, string fileExtension)
{
	int i = 1;
	ifstream readFile;
	bool end = false;	

	while(!end)
	{
		stringstream ss;
		ss << folderDir << _actionPrefix << i << fileExtension;
		string file = ss.str();

		bool exists = false;
		ifstream f(file.c_str());
		if (f.good()) 
		{
			exists =  true;
			i++;
		}
		else
		{
			end = true;
		}
		f.close();
		ss.clear();
	}

	return i - 1;

}

string PolicyFileParser::GetCurrDirectory()
{
	return _policyFilesDir;
}

//get the number of time steps in folder
int PolicyFileParser::GetNumerOfTimeSteps()
{
	return GetNumberOfFilesInFolder(_policyFilesDir, _fileExtension);
}